package com.liuyi.netty.im.lychat.config;

import cn.hutool.core.collection.CollUtil;
import com.github.xiaoymin.knife4j.spring.extension.OpenApiExtensionResolver;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.RequestAttribute;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.AuthorizationScope;
import springfox.documentation.service.SecurityReference;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;

import java.util.Collections;
import java.util.List;
import java.util.function.Predicate;

/**
 * swagger 公用配置，可以继承这个类
 */
@Slf4j
public abstract class SwaggerAbstractConfig {

    /*引入Knife4j提供的扩展类*/
    @Getter
    private final OpenApiExtensionResolver openApiExtensionResolver;

    @Autowired
    public SwaggerAbstractConfig(OpenApiExtensionResolver openApiExtensionResolver) {
        this.openApiExtensionResolver = openApiExtensionResolver;
    }

    /**
     * 定义接口授权
     * 定义那些接口需要登录才可访问，这里UI界面会给一个小标志
     * 如果不需要，这个就不用管，或者连带着 {@linkplain #securitySelector()}删除即可
     */
    protected List<SecurityContext> securityContexts() {
        final SecurityReference globalReference = new SecurityReference("global",
                new AuthorizationScope[]{new AuthorizationScope("global", "全局的")});
        return Collections.singletonList(
                SecurityContext.builder()
                        .securityReferences(CollUtil.newArrayList(globalReference))
                        .forPaths(securitySelector())
                        .build()
        );
    }

    /**
     * 定义那些时需要授权才可访问的接口
     * <pre>
     *  //定义 url1或者url2都属于授权才可以访问 api路径
     *  return PathSelectors.ant(url1).or(PathSelectors.ant(url2));
     * </pre>
     */
    protected abstract Predicate<String> securitySelector();

    /**
     * docker 对象快速创建
     */
    protected Docket docketRapid(String groupName, String title, String description, String basePackage) {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(new ApiInfoBuilder()
                        .title(title)
                        .description(description)
                        .version(getVersion())
                        .build())
                //分组名称
                .groupName(groupName)
                .select()
                //这里指定Controller扫描包路径
                .apis(RequestHandlerSelectors.basePackage(basePackage)
                        .and(RequestHandlerSelectors.withClassAnnotation(Api.class))
                        .and(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)))
                .paths(PathSelectors.any())
                .build()
                .ignoredParameterTypes(RequestAttribute.class)    // 排除扫描 @RequestAttribute 注解
                .ignoredParameterTypes(Errors.class)
                .securityContexts(securityContexts())
                .extensions(getOpenApiExtensionResolver().buildExtensions(groupName));
    }

    /**
     * 项目版本
     */
    protected abstract String getVersion();
}
